module main;
/*
 * przedmiot: Język D
 * program: W trakcie kompilacji
 * autor: Paweł Piątkowski (pablo@mat.umk.pl)
 * 
 * 5 prostych problemów
 *  - obliczanie n!
 *  - obliczanie n-tej liczby fibonacci'ego
 *  - obliczanie funkcji ackermanna
 *  - znalezienie najmniejszej liczby w tablicy
 *  - znalezienie największej liczby w tablicy
*/

import std.stdio;

/** oblicza n! */
template silnia(int n) {
	static if (n < 2)
		const int s = 1;
	else
		const int s = n * silnia!(n - 1).s;
	}

/** oblicza n-tą liczbę Fibonacci'ego */
template fibonacci(int n){
	static if (n == 0) {
		const int f = 0;
	} else static if (n == 1) {
		const int f = 1;
	} else {
		const int f = fibonacci!(n-1).f + fibonacci!(n-2).f;
	}
}

/** funkcja Ackermanna */
template ackermann(int m, int n){
	static if(m == 0) {
		const int a = n + 1;
	} else static if (m > 0 && n == 0) {
		const int a = ackermann!(m - 1, 1).a;
	} else static if (m > 0 && n > 0) {
		const int a = ackermann!(m - 1, ackermann!(m, n - 1).a).a;
	}
}

/** zwraca mniejszą z dwóch wartości */
T min(T)(T a, T b)
{
	return (a < b ? a : b);
}

/** zwraca większą z dwóch wartości */
T max(T)(T a, T b)
{
	return (a > b ? a : b);
}

const int[] tab = [-1, 4, 2, -5, 7];

/** wyznacza najmniejszą wartość z tablicy tab */
template minVal(int index = 0)
{
	static if ( (index + 1) == tab.length ){
		const int m = tab[index];
	}else{
		const int m = min!(int)(tab[index], minVal!(index + 1).m);
	}
}

/** wyznacza największą wartość z tablicy tab */
template maxVal(int index = 0)
{
	static if ( (index + 1) == tab.length ){
		const int m = tab[index];
	}else{
		const int m = max!(int)(tab[index], maxVal!(index + 1).m);
	}
}


void main()
{
	/* problem 1 - silnia */
	mixin silnia!(5) silnia_mixin;
	writefln("silnia(5): %d\n",silnia!(5).s);
	assert(silnia!(1).s == 1, "silnia error");
	assert(silnia!(2).s == 2, "silnia error");
	assert(silnia!(3).s == 6, "silnia error");
	assert(silnia!(4).s == 24, "silnia error");
	assert(silnia!(5).s == 120, "silnia error");
	
	/* problem 2 - fibonacci */
	mixin fibonacci!(5) fibonacci_mixin;
	writefln("fibonacci(5): %d\n",fibonacci_mixin.f);
	assert(fibonacci!(0).f == 0, "fibonacci error");
	assert(fibonacci!(1).f == 1, "fibonacci error");
	assert(fibonacci!(2).f == 1, "fibonacci error");
	assert(fibonacci!(3).f == 2, "fibonacci error");
	assert(fibonacci!(4).f == 3, "fibonacci error");
	assert(fibonacci!(5).f == 5, "fibonacci error");
	assert(fibonacci!(6).f == 8, "fibonacci error");
	
	/* problem 3 - ackermann */
	mixin ackermann!(3, 3) ackermann_mixin;
	writefln("ackermann(3, 3): %d\n",ackermann_mixin.a);
	assert(ackermann!(0, 0).a == 1, "ackermann error");
	assert(ackermann!(1, 0).a == 2, "ackermann error");
	assert(ackermann!(2, 0).a == 3, "ackermann error");
	assert(ackermann!(3, 0).a == 5, "ackermann error");
	assert(ackermann!(4, 0).a == 13, "ackermann error");
	assert(ackermann!(3, 3).a == 61, "ackermann error");
	assert(ackermann!(3, 4).a == 125, "ackermann error");
	
	
	writefln("tablica: ", tab);
	/* problem 4 - znajdowanie minimum w tablicy */
	mixin minVal!() minVal_mixin;
	writefln("minVal: %d", minVal_mixin.m);
	
	/* problem 4 - znajdowanie maximum w tablicy */
	mixin maxVal!() maxVal_mixin;
	writefln("maxVal: %d", maxVal_mixin.m);
}